/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.android.apps.mytracks.services.sensors;

import com.google.android.apps.mytracks.content.Sensor;

/**
 * An implementation of a Sensor MessageParser for Polar Wearlink Bluetooth HRM.
 * Polar Bluetooth Wearlink packet example; Hdr Len Chk Seq Status HeartRate
 * RRInterval_16-bits FE 08 F7 06 F1 48 03 64 where; Hdr always = 254 (0xFE),
 * Chk = 255 - Len Seq range 0 to 15 Status = Upper nibble may be battery
 * voltage bit 0 is Beat Detection flag. Additional packet examples; FE 08 F7 06
 * F1 48 03 64 FE 0A F5 06 F1 48 03 64 03 70
 * 
 * @author John R. Gerthoffer
 * @author Nico Laum <nic@nclm.de>
 */
public class PolarMessageParser implements MessageParser {
  public static final int MIN_FRAME_SIZE = 8;
  public static final int MAX_FRAME_SIZE = 18;

  /**
   * Applies Polar packet validation rules to buffer. Polar packets are checked
   * for following; offset 0 = header byte, 254 (0xFE). offset 1 = packet length
   * byte, 8, 10, 12, 14. offset 2 = check byte, 255 - packet length. offset 3 =
   * sequence byte, range from 0 to 15.
   * 
   * @param an array of bytes to parse
   * @param the number of fresh read bytes in buffer
   * @param buffer offset to beginning of packet.
   * @return whether buffer has a valid packet at offset i
   */
  private boolean packetValid(byte[] buffer, int length, int i) {
    boolean headerValid = (buffer[i] & 0xFF) == 0xFE;
    boolean lengthValid = length > ((buffer[i + 1] & 0xFF) - 1);
    boolean checkbyteValid = (buffer[i + 2] & 0xFF) == (0xFF - (buffer[i + 1] & 0xFF));
    boolean sequenceValid = (buffer[i + 3] & 0xFF) < 16;

    return headerValid && lengthValid && checkbyteValid && sequenceValid;
  }

  /**
   * @param an array of bytes to parse
   * @param the number of fresh read bytes in buffer
   * @return the Sensor.SensorDataSet including parsed
   */
  @Override
  public Sensor.SensorDataSet parseBuffer(byte[] buffer, int length) {

    int heartRate = 0;

    Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder();
    if (packetValid(buffer, length, 0)) {
      heartRate = buffer[5] & 0xFF;

      // Heart Rate
      b.setValue(heartRate).setState(Sensor.SensorState.SENDING);
    }
    Sensor.SensorDataSet sds = Sensor.SensorDataSet.newBuilder()
        .setCreationTime(System.currentTimeMillis()).setHeartRate(b).build();

    return sds;
  }

  /**
   * Applies packet validation rules to buffer
   * 
   * @param an array of bytes to parse
   * @param the number of fresh read bytes in buffer
   * @return whether buffer has a valid packet starting at index zero
   */
  @Override
  public boolean isValid(byte[] buffer, int length) {
    return packetValid(buffer, length, 0);
  }

  /**
   * Polar uses variable packet sizes; 8, 10, 12, 14 and rarely 16. The most
   * frequent are 8 and 10. We will wait for 8 bytes, then we should try to
   * parse the frame. This way, we are assured of getting one good one.
   * 
   * @return the minimum size of a frame
   */
  @Override
  public int getMinimumFrameSize() {
    return MIN_FRAME_SIZE;
  }

  @Override
  public int getMaximumFrameSize() {
    return MAX_FRAME_SIZE;
  }

  /**
   * Searches buffer for the beginning of a valid packet.
   * 
   * @param an array of bytes to parse
   * @param the number of fresh read bytes in buffer
   * @return index to beginning of good packet, or -1 if none found.
   */
  @Override
  public int findNextAlignment(byte[] buffer, int length) {
    // Minimum length Polar packets is 8, so stop search 8 bytes before buffer
    // ends.
    for (int i = 0; i < length - MIN_FRAME_SIZE; i++) {
      if (packetValid(buffer, length, i)) { return i; }
    }
    return -1;
  }
}
